package me.illtamer.infinite.file.loader;

import lombok.extern.slf4j.Slf4j;
import me.illtamer.infinite.config.Configuration;
import me.illtamer.infinite.factory.SingletonFactory;
import me.illtamer.infinite.file.FileType;
import me.illtamer.infinite.file.parser.TypeParser;
import me.illtamer.infinite.file.source.FileSource;
import me.illtamer.infinite.file.source.LocalFileSource;
import me.illtamer.infinite.pojo.Global;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 文件加载类
 * */
@Slf4j
public class FileManager {

    static final LinkedHashMap<String, File> WAITING_FILES = new LinkedHashMap<>();

    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            0,
            Runtime.getRuntime().availableProcessors(),
            60L,
            TimeUnit.SECONDS,
            new SynchronousQueue<>(),
            new BasicThreadFactory.Builder().namingPattern("File-Parser-Pool-%d").build());

    /**
     * 主动加载 source 目录下文件
     * @apiNote 在 {@link #addFileParse(TypeParser)} 方法后调用
     * */
    public static void loadSourceFiles() {
        Set<FileType> fileTypes = DispatchFileParserHandler.PARSER_MAP.keySet();
        File[] files = Configuration.SOURCE_FOLDER.listFiles((dir, name) -> {
            int index;
            if ((index = name.indexOf(".")) == -1 || index + 1 == name.length()) return false;
            String suffix = name.substring(index + 1).toLowerCase(Locale.ROOT);
            for (FileType type : fileTypes)
                if (type.getSuffix().equals(suffix) || type.getAlias().contains(suffix)) return true;
            return false;
        });
        if (files == null || files.length == 0) return;
        Arrays.stream(files).forEach(file -> WAITING_FILES.put(file.getName(), file));
        log.info("Load {} files from source folder", files.length);
    }

    /**
     * 添加类型处理器
     * */
    @Nullable
    public static TypeParser addFileParse(TypeParser parser) {
        TypeParser put = DispatchFileParserHandler.PARSER_MAP.put(parser.parserType(), parser);
        if (put != null)
            log.warn("The parser bond to {} is replaced by a new one({}) !", put.parserType(), parser);
        return put;
    }

    /**
     * 将经选择的文件分发给对应类型的处理器
     * @param inputFileNames 需要翻译的文件名称
     * @apiNote {@link DispatchFileParserHandler}
     * */
    public static void dispatchFileParser(Set<String> inputFileNames) {
        doDispatchFileParser(WAITING_FILES.entrySet().stream().filter(entry ->
                inputFileNames.contains(entry.getKey())).map(Map.Entry::getValue).collect(Collectors.toSet()));
    }

    /**
     * 将所有文件分发给对应类型的处理器
     * @apiNote {@link DispatchFileParserHandler}
     * */
    public static void dispatchFileParser() {
        doDispatchFileParser(WAITING_FILES.values());
    }

    private static void doDispatchFileParser(Collection<File> files) {
        log.info("Find {} files, task executor starts ->", files.size());
        List<Future<Boolean>> futures = new ArrayList<>(files.size());
        for (File file : files) {
            Future<Boolean> future = EXECUTOR.submit(() -> {
                FileSource source = new LocalFileSource(file);
                DispatchFileParserHandler handler = new DispatchFileParserHandler();
                return handler.handle(source);
            });
            futures.add(future);
        }
        int count = 0;
        Global global = SingletonFactory.getSingleton(Global.class);
        long start = System.currentTimeMillis();
        long end = start;
        while (futures.size() != 0 && (end - start) / 1000 < global.getMaxTaskCost()) {
            Iterator<Future<Boolean>> iterator = futures.iterator();
            if (iterator.hasNext()) {
                Future<Boolean> future = iterator.next();
                if (future.isDone()) {
                    ++ count;
                    futures.remove(future);
                }
            }
            end = System.currentTimeMillis();
        }
        if (count == files.size())
            log.info("All translates success !");
        else
            log.warn("Just {} translates return during the max-task-cost({})", count, global.getMaxTaskCost());
    }

    public static Set<String> linkedFileNameSet() {
        return WAITING_FILES.keySet();
    }

}
